package com.jzo2o.orders.manager.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jzo2o.api.customer.dto.response.AddressBookResDTO;
import com.jzo2o.api.foundations.dto.response.ServeAggregationResDTO;
import com.jzo2o.api.trade.NativePayApi;
import com.jzo2o.api.trade.TradingApi;
import com.jzo2o.api.trade.dto.request.NativePayReqDTO;
import com.jzo2o.api.trade.dto.response.NativePayResDTO;
import com.jzo2o.api.trade.dto.response.TradingResDTO;
import com.jzo2o.api.trade.enums.PayChannelEnum;
import com.jzo2o.api.trade.enums.TradingStateEnum;
import com.jzo2o.common.constants.ErrorInfo;
import com.jzo2o.common.expcetions.CommonException;
import com.jzo2o.common.model.msg.TradeStatusMsg;
import com.jzo2o.common.utils.BeanUtils;
import com.jzo2o.common.utils.DateUtils;
import com.jzo2o.common.utils.NumberUtils;
import com.jzo2o.common.utils.ObjectUtils;
import com.jzo2o.mvc.utils.UserContext;
import com.jzo2o.orders.base.config.OrderStateMachine;
import com.jzo2o.orders.base.constants.RedisConstants;
import com.jzo2o.orders.base.enums.OrderPayStatusEnum;
import com.jzo2o.orders.base.enums.OrderStatusChangeEventEnum;
import com.jzo2o.orders.base.enums.OrderStatusEnum;
import com.jzo2o.orders.base.mapper.OrdersMapper;
import com.jzo2o.orders.base.model.domain.Orders;
import com.jzo2o.orders.base.model.dto.OrderSnapshotDTO;
import com.jzo2o.orders.manager.model.dto.request.OrdersPayReqDTO;
import com.jzo2o.orders.manager.model.dto.request.PlaceOrderReqDTO;
import com.jzo2o.orders.manager.model.dto.response.OrdersPayResDTO;
import com.jzo2o.orders.manager.model.dto.response.PlaceOrderResDTO;
import com.jzo2o.orders.manager.porperties.TradeProperties;
import com.jzo2o.orders.manager.service.IOrdersCreateService;
import com.jzo2o.orders.manager.service.client.CustomerClient;
import com.jzo2o.orders.manager.service.client.ServeClient;
import com.jzo2o.redis.helper.CacheHelper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;

import static com.jzo2o.orders.base.constants.RedisConstants.RedisKey.ORDERS;

/**
 * <p>
 * 下单服务类
 * </p>
 *
 * @author itcast
 * @since 2023-07-10
 */
@Slf4j
@Service
public class OrdersCreateServiceImpl extends ServiceImpl<OrdersMapper, Orders> implements IOrdersCreateService {

    @Autowired
    private ServeClient serveClient;

    @Autowired
    private CustomerClient customerClient;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private IOrdersCreateService orderService;

    @Autowired
    private TradeProperties tradeProperties;
    @Autowired
    private NativePayApi nativePayApi;

    @Autowired
    private TradingApi tradingApi;

    @Autowired
    private OrderStateMachine orderStateMachine;

    @Autowired
    private CacheHelper cacheHelper;
    /**
     * 查询超时订单id列表
     *
     * @param count 数量
     * @return 订单id列表
     */
    @Override
    public List<Orders> queryOverTimePayOrdersListByCount(Integer count) {
        //根据订单创建时间查询超过15分钟未支付的订单
        List<Orders> list = lambdaQuery()
                //查询待支付状态的订单
                .eq(Orders::getOrdersStatus, OrderStatusEnum.NO_PAY.getStatus())
                //小于当前时间减去15分钟，即待支付状态已过15分钟
                .lt(Orders::getCreateTime, LocalDateTime.now().minusMinutes(15))
                .last("limit " + count)
                .list();
        return list;
    }

    @Override
    public OrdersPayResDTO getPayResultFromTradServer(Long id) {
        //查询订单表
        Orders orders = baseMapper.selectById(id);
        if (ObjectUtil.isNull(orders)) {
            throw new CommonException(ErrorInfo.Code.TRADE_FAILED, "订单不存在");
        }
        //支付结果
        Integer payStatus = orders.getPayStatus();

        //未支付且已存在支付服务的交易单号此时远程调用支付服务查询支付结果
        if (OrderPayStatusEnum.NO_PAY.getStatus() == payStatus && ObjectUtil.isNotEmpty(orders.getTradingOrderNo())) {
            //远程调用支付服务查询支付结果
            TradingResDTO tradingResDTO = tradingApi.findTradResultByTradingOrderNo(orders.getTradingOrderNo());
            //如果支付成功这里更新订单状态
            if (ObjectUtil.isNotNull(tradingResDTO)
                    && ObjectUtil.equals(tradingResDTO.getTradingState(), TradingStateEnum.YJS)) {
                //设置订单的支付状态成功
                TradeStatusMsg msg = TradeStatusMsg.builder()
                        .productOrderNo(orders.getId())
                        .tradingChannel(tradingResDTO.getTradingChannel())
                        .statusCode(TradingStateEnum.YJS.getCode())
                        .tradingOrderNo(tradingResDTO.getTradingOrderNo())
                        .transactionId(tradingResDTO.getTransactionId())
                        .build();
                orderService.paySuccess(msg);
                //构造返回数据
                OrdersPayResDTO ordersPayResDTO = BeanUtils.toBean(msg, OrdersPayResDTO.class);
                ordersPayResDTO.setPayStatus(OrderPayStatusEnum.PAY_SUCCESS.getStatus());
                return ordersPayResDTO;
            }
        }
        OrdersPayResDTO ordersPayResDTO = new OrdersPayResDTO();
        ordersPayResDTO.setPayStatus(payStatus);
        ordersPayResDTO.setProductOrderNo(orders.getId());
        ordersPayResDTO.setTradingOrderNo(orders.getTradingOrderNo());
        ordersPayResDTO.setTradingChannel(orders.getTradingChannel());
        return ordersPayResDTO;
    }

    /**
     * 更新支付最终状态
     * @param tm 交易状态消息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void paySuccess(TradeStatusMsg tm) {
        //1、判断订单是否存在
        Orders orders = getById(tm.getProductOrderNo());
        if(ObjectUtil.isEmpty(orders)){
            throw new CommonException("订单不存在！");
        }

        //2、订单如果不是待支付状态，不做任何处理
        if(ObjectUtil.notEqual(orders.getPayStatus(),OrderPayStatusEnum.NO_PAY.getStatus()) && ObjectUtil.notEqual(orders.getOrdersStatus(),OrderStatusEnum.NO_PAY.getStatus())){
            log.info("当前订单已完成状态修改！");
            return;
        }

        //3、是否缺少第三方交易单号
        log.info("第三方交易单号：{}",tm.getTransactionId());
        if(ObjectUtil.isEmpty(tm.getTransactionId())){
            throw new CommonException("订单状态已修改，但未检测到第三方交易单号！");
        }

        // 修改订单状态和支付状态
        OrderSnapshotDTO orderSnapshotDTO = OrderSnapshotDTO.builder()
                .payTime(LocalDateTime.now())
                .tradingOrderNo(tm.getTradingOrderNo())
                .tradingChannel(tm.getTradingChannel())
                .thirdOrderId(tm.getTransactionId())
                .build();
        orderStateMachine.changeStatus(orders.getUserId(), String.valueOf(orders.getId()), OrderStatusChangeEventEnum.PAYED, orderSnapshotDTO);

        //4、执行修改
        Integer statusCode = tm.getStatusCode();
        boolean update = lambdaUpdate()
                .set(ObjectUtil.equal(statusCode, TradingStateEnum.YJS.getCode()),Orders::getOrdersStatus, OrderStatusEnum.DISPATCHING.getStatus())
                .set(ObjectUtil.equal(statusCode, TradingStateEnum.FKSB.getCode()),Orders::getOrdersStatus, OrderStatusEnum.CLOSED.getStatus())
                .set(ObjectUtil.equal(statusCode, TradingStateEnum.QXDD.getCode()),Orders::getOrdersStatus, OrderStatusEnum.CANCELED.getStatus())
                .set(ObjectUtil.equal(statusCode, TradingStateEnum.YJS.getCode()),Orders::getPayStatus, OrderPayStatusEnum.PAY_SUCCESS.getStatus())
                .set(ObjectUtil.isNotEmpty(tm.getTransactionId()),Orders::getTransactionId, tm.getTransactionId())
                .eq(Orders::getId, tm.getProductOrderNo())
                .update();
        cacheHelper.remove(String.format(ORDERS, orders.getUserId()));
        if(!update){
            log.info("更新订单:{}支付成功失败", orders.getId());
            throw new CommonException("更新订单"+orders.getId()+"支付成功失败");
        }

    }

    /**
     * 订单支付
     * @param id              订单id
     * @param ordersPayReqDTO 订单支付请求体
     * @return 订单支付响应体
     */
    @Override
    public OrdersPayResDTO pay(Long id, OrdersPayReqDTO ordersPayReqDTO) {
        //1、检查订单是否存在
        Orders orders = getById(id);
        if(ObjectUtil.isEmpty(orders)){
            throw new CommonException("订单不存在！");
        }

        //2、订单如果已经支付，直接返回
        if(orders.getPayStatus()==OrderPayStatusEnum.PAY_SUCCESS.getStatus() && ObjectUtil.isNotEmpty(orders.getTransactionId())){
            OrdersPayResDTO ordersPayResDTO = new OrdersPayResDTO();
            BeanUtil.copyProperties(ordersPayReqDTO,ordersPayResDTO);
            ordersPayResDTO.setProductOrderNo(orders.getId());
            return ordersPayResDTO;
        }else{
            //3、未支付，生成二维码
            NativePayResDTO nativePayResDTO = generateQrCode(orders, ordersPayReqDTO.getTradingChannel());
            OrdersPayResDTO ordersPayResDTO = BeanUtil.toBean(nativePayResDTO, OrdersPayResDTO.class);
            return ordersPayResDTO;
        }
    }

    /**
     * 创建支付交易记录，获取支付二维码
     * @param orders
     * @param tradingChannel
     * @return
     */
    public NativePayResDTO generateQrCode(Orders orders, PayChannelEnum tradingChannel){
        //判断支付渠道
        Long enterpriseId = ObjectUtil.equal(PayChannelEnum.ALI_PAY, tradingChannel) ?
                tradeProperties.getAliEnterpriseId() : tradeProperties.getWechatEnterpriseId();

        //构建支付请求参数
        NativePayReqDTO nativePayReqDTO = new NativePayReqDTO();
        //商户号
        nativePayReqDTO.setEnterpriseId(enterpriseId);
        //业务系统标识
        nativePayReqDTO.setProductAppId("jzo2o.orders");
        //家政订单号
        nativePayReqDTO.setProductOrderNo(orders.getId());
        //支付渠道
        nativePayReqDTO.setTradingChannel(tradingChannel);
        //支付金额
//        nativePayReqDTO.setTradingAmount(orders.getRealPayAmount());
        nativePayReqDTO.setTradingAmount(new BigDecimal(0.01));
        //备注信息
        nativePayReqDTO.setMemo(orders.getServeItemName());
        //判断是否切换支付渠道
        if (ObjectUtil.isNotEmpty(orders.getTradingChannel())
                && ObjectUtil.notEqual(orders.getTradingChannel(), tradingChannel.toString())) {
            nativePayReqDTO.setChangeChannel(true);
        }
        //生成支付二维码
        NativePayResDTO downLineTrading = nativePayApi.createDownLineTrading(nativePayReqDTO);
        if(ObjectUtils.isNotNull(downLineTrading)){
            log.info("订单:{}请求支付,生成二维码:{}",orders.getId(),downLineTrading.toString());
            //将二维码更新到交易订单中
            boolean update = lambdaUpdate()
                    .eq(Orders::getId, downLineTrading.getProductOrderNo())
                    .set(Orders::getTradingOrderNo, downLineTrading.getTradingOrderNo())
                    .set(Orders::getTradingChannel, downLineTrading.getTradingChannel())
                    .update();
            if(!update){
                throw new CommonException("下单支付失败！");
            }
        }
        return downLineTrading;
    }

    /**
     * 生成订单id 格式：{yyMMdd}{13位id}
     */

    private Long generateOrderId(){
        //1、获取自增ID值
        Long increment = redisTemplate.opsForValue().increment(RedisConstants.Lock.ORDERS_SHARD_KEY_ID_GENERATOR, 1);

        //2、2位年+2位月+2位日+13位序号
        Long orderId = DateUtils.getFormatDate(LocalDateTime.now(),"yyMMdd") * 10000000000000L+increment;
        return orderId;
    }

    /**
     * 用户下单
     * @param placeOrderReqDTO
     * @return
     */
    @Override
    public PlaceOrderResDTO placeOrder(PlaceOrderReqDTO placeOrderReqDTO) {
        //1、下单人信息获取
        AddressBookResDTO addressBook = customerClient.getDetail(placeOrderReqDTO.getAddressBookId());
        if(ObjectUtil.isEmpty(addressBook)){
            throw new CommonException("服务地址不能为空！");
        }

        //2、服务信息获取
        ServeAggregationResDTO serveResDTO  = serveClient.getDetail(placeOrderReqDTO.getServeId());
        if(ObjectUtil.isEmpty(serveResDTO) || ObjectUtil.notEqual(serveResDTO.getSaleStatus(),2)){
            throw new CommonException("服务不可用！");
        }

        //2.1订单初始化数据
        Orders orders = new Orders();
        // id 订单id
        orders.setId(generateOrderId());
        // userId，从threadLocal获取当前登录用户的id，通过UserContextInteceptor拦截进行设置
        orders.setUserId(UserContext.currentUserId());
        // 服务id
        orders.setServeId(placeOrderReqDTO.getServeId());
        // 服务项id
        orders.setServeItemId(serveResDTO.getServeItemId());
        orders.setServeItemName(serveResDTO.getServeItemName());
        orders.setServeItemImg(serveResDTO.getServeItemImg());
        orders.setUnit(serveResDTO.getUnit());
        //服务类型信息
        orders.setServeTypeId(serveResDTO.getServeTypeId());
        orders.setServeTypeName(serveResDTO.getServeTypeName());
        // 订单状态
        orders.setOrdersStatus(0);
        // 支付状态，暂不支持，初始化一个空状态
        orders.setPayStatus(OrderPayStatusEnum.NO_PAY.getStatus());
        // 服务时间
        orders.setServeStartTime(placeOrderReqDTO.getServeStartTime());
        // 城市编码
        orders.setCityCode(serveResDTO.getCityCode());
        // 地理位置
        orders.setLon(addressBook.getLon());
        orders.setLat(addressBook.getLat());

        //省-市-区-地-址
        String serveAddress = new StringBuffer(addressBook.getProvince())
                .append(addressBook.getCity())
                .append(addressBook.getCounty())
                .append(addressBook.getAddress())
                .toString();
        orders.setServeAddress(serveAddress);
        // 联系人
        orders.setContactsName(addressBook.getName());
        orders.setContactsPhone(addressBook.getPhone());

        // 价格
        orders.setPrice(serveResDTO.getPrice());
        // 购买数量
        orders.setPurNum(NumberUtils.null2Default(placeOrderReqDTO.getPurNum(), 1));
        // 订单总金额 价格 * 购买数量
        orders.setTotalAmount(orders.getPrice().multiply(new BigDecimal(orders.getPurNum())));

        // 优惠金额 当前默认0
        orders.setDiscountAmount(BigDecimal.ZERO);
        // 实付金额 订单总金额 - 优惠金额
        orders.setRealPayAmount(NumberUtils.sub(orders.getTotalAmount(), orders.getDiscountAmount()));
        //排序字段,根据服务开始时间转为毫秒时间戳+订单后5位
        long sortBy = DateUtils.toEpochMilli(orders.getServeStartTime()) + orders.getId() % 100000;
        orders.setSortBy(sortBy);
        //boolean save = this.save(orders);
        //if(!save){
        //    throw new CommonException("添加订单失败！");
        //}

        //添加订单
        orderService.add(orders);
        return new PlaceOrderResDTO(orders.getId());
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void add(Orders orders) {
        boolean save = this.save(orders);
        if(!save){
            throw new CommonException("下单失败！");
        }
        //构建快照对象
        OrderSnapshotDTO orderSnapshotDTO = BeanUtil.toBean(baseMapper.selectById(orders.getId()), OrderSnapshotDTO.class);
        //状态机启动
        orderStateMachine.start(orders.getUserId(),String.valueOf(orders.getId()),orderSnapshotDTO);
    }
}
